/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package neuralnetwork;

import org.neuroph.core.learning.DataSet;
import org.neuroph.core.learning.DataSetRow;
import org.neuroph.nnet.MultiLayerPerceptron;
import org.neuroph.nnet.learning.MomentumBackpropagation;
import org.neuroph.util.TransferFunctionType;

/**
 *
 * @author Celso
 */
public class NeuralNetwork {

    MultiLayerPerceptron neuralNet;
    MomentumBackpropagation learningRule;

    /**
     * Create a new MLP Neural Network
     *
     * @param inputsCount
     * @param hiddenNeurons
     * @param outputsCount
     * @param transferFunctionType
     */
    public NeuralNetwork(
            Integer inputsCount,
            Integer hiddenNeurons,
            Integer outputsCount,
            TransferFunctionType transferFunctionType) {

        neuralNet = new MultiLayerPerceptron(inputsCount, hiddenNeurons, outputsCount);
        learningRule = (MomentumBackpropagation) neuralNet.getLearningRule();

    }

    /**
     * set the learning rate
     *
     * @param learningRate
     */
    public void setLearningRate(Double learningRate) {
        learningRule.setLearningRate(learningRate);
    }

    public void setMomentum(Double momentum) {
        learningRule.setMomentum(momentum);
    }

    public void setMaxError(Double maxError) {
        learningRule.setMaxError(maxError);
    }

    public void setMaxInteraction(Integer maxIterations) {
        learningRule.setMaxIterations(maxIterations);
    }

    /**
     * execute learning algorithm
     *
     * @param trainingSet - a training DataSet
     */
    public void learn(DataSet trainingSet) {
        System.out.println("\n ... Training neural network ...");
        neuralNet.learn(trainingSet);
        System.out.println("Done!\n");
    }

      /**
     * execute learning algorithm
     *
     * @param testingSet a testing DataSet
     * @return a number of error in testing made by network
     */
    public Integer test(DataSet testingSet) {
        double[] desiredOutput;
        double[] networkOutput;
        Integer totalError = 0;

        System.out.println("\n ... Testing ...");
        for (DataSetRow testElement : testingSet.getRows()) {

            desiredOutput = testElement.getDesiredOutput();
            neuralNet.setInput(testElement.getInput());
            neuralNet.calculate();
            networkOutput = neuralNet.getOutput();
            totalError += evaluate(desiredOutput, networkOutput);
        }
        System.out.println("Done!\n");
        return totalError;

    }

    private Integer evaluate(double[] desiredOutput, double[] networkOutput) {
        Integer error = Math.round((float) desiredOutput[0])
                - Math.round((float) networkOutput[0]);
        error = (int) Math.pow(error, 2);

        return error;
    }

    public Double getPreviousEpochError() {

        return learningRule.getPreviousEpochError();
    }

}
